﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using AmazonGlacierGUI.Core;
using AmazonGlacierGUI.Core.Helpers;
using AmazonGlacierGUI.Core.Helpers.AmazonGlacier;
using AmazonGlacierGUI.Core.Infrastructure;
using AmazonGlacierGUI.Core.Model;
using AmazonGlacierGUI.UI.Infrastructure;
using AmazonGlacierGUI.UI.Infrastructure.MVVMFramework;
using GalaSoft.MvvmLight.Command;
using Timer = System.Timers.Timer;

namespace AmazonGlacierGUI.UI.ViewModels
{
    //26-Aug-2012 Priyan R
    public class JobViewModel : AppViewModelBase
    {
        public event EventHandler OnQueued;
        public event EventHandler OnRemovRequest;
        private AmazonGlacierUploadHelper _uploader = new AmazonGlacierUploadHelper();
        private AmazonGlacierDownloadHelper _downloader = new AmazonGlacierDownloadHelper();
        private string _jobId;
        private string _uploadId;
        //private int _tryCount = 0;
        //private const int MAX_TRY = 5;
        private DateTime _createdOn;
        private DateTime? _completedOn;
        private long _uploadedBytes { get; set; }
        private long _downloadedBytes { get; set; }
        private Exception _lastException;
        private Timer _downloadRequestCheckTimer = new Timer();
        public JobViewModel()
        {
            _uploader.OnComplete += new EventHandler<AmazonGlacierUploadHelper.CompletedEventArgs>(_uploader_OnComplete);
            _uploader.OnError += new EventHandler<AmazonGlacierUploadHelper.ErrorEventArgs>(_uploader_OnError);
            _uploader.OnProgress += new EventHandler<AmazonGlacierUploadHelper.ProgressEventArgs>(_uploader_OnProgress);
            _uploader.OnMultiPartInitiated += new EventHandler<AmazonGlacierUploadHelper.MultiPartInitiatedEventArgs>(_uploader_OnMultiPartInitiated);
            _uploader.OnCancancelled += new EventHandler(_uploader_OnCancelled);
            _downloader.OnComplete += new EventHandler<AmazonGlacierDownloadHelper.CompletedEventArgs>(_downloader_OnComplete);
            _downloader.OnError += new EventHandler<AmazonGlacierDownloadHelper.ErrorEventArgs>(_downloader_OnError);
            _downloader.OnProgress += new EventHandler<AmazonGlacierDownloadHelper.ProgressEventArgs>(_downloader_OnProgress);
            _downloader.OnCancelled += new EventHandler(_downloader_OnCancelled);
            _downloadRequestCheckTimer.Interval = (1000 * 60) * 30;
            _downloadRequestCheckTimer.Elapsed += new ElapsedEventHandler(_downloadRequestCheckTimer_Elapsed);
            _downloadRequestCheckTimer.Enabled = false;
            WireCommands();

        }

        void _uploader_OnCancelled(object sender, EventArgs e)
        {
            JobStatus = JobStatusEnum.Paused;
            SaveJob();
        }

        void _downloader_OnCancelled(object sender, EventArgs e)
        {
            JobStatus = JobStatusEnum.Paused;
            SaveJob();

        }

        private void _downloadRequestCheckTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            CheckDownloadRequestStatus();
        }


        void _uploader_OnMultiPartInitiated(object sender, AmazonGlacierUploadHelper.MultiPartInitiatedEventArgs e)
        {
            _uploadId = e.UploadId;
            SaveJob();
        }
        void _uploader_OnProgress(object sender, AmazonGlacierUploadHelper.ProgressEventArgs e)
        {
            Progress = Math.Round(e.UploadedBytes / (double)_uploader.FileSize * 100, 2);
            var prev = _uploadedBytes;
            _uploadedBytes = e.UploadedBytes;
            if (_uploadedBytes - prev > 1024 * 50)
                SaveJob();
            RaisePropertyChanged(() => StatusMessage);
        }

        void _uploader_OnError(object sender, AmazonGlacierUploadHelper.ErrorEventArgs e)
        {
            _lastException = new Exception(e.ErrorMessage);
            JobStatus = JobStatusEnum.Error;
            SaveJob();
        }

        void _uploader_OnComplete(object sender, AmazonGlacierUploadHelper.CompletedEventArgs e)
        {
            JobStatus = JobStatusEnum.Completed;
            _completedOn = AppUtility.GetDate();
            SaveJob();
            SaveUploadHistory(e.ArchiveId);
        }
        void _downloader_OnProgress(object sender, AmazonGlacierDownloadHelper.ProgressEventArgs e)
        {
            Progress = Math.Round(e.DownloadedBytes / (double)e.TotalBytes * 100, 2);
            var prev = _downloadedBytes;
            _downloadedBytes = e.DownloadedBytes;
            if (_downloadedBytes - prev > 1024 * 50)
                SaveJob();
            RaisePropertyChanged(() => StatusMessage);
        }

        void _downloader_OnError(object sender, AmazonGlacierDownloadHelper.ErrorEventArgs e)
        {
            _lastException = new Exception(e.ErrorMessage);
            JobStatus = JobStatusEnum.Error;
            SaveJob();

        }
        void _downloader_OnComplete(object sender, AmazonGlacierDownloadHelper.CompletedEventArgs e)
        {
            JobStatus = JobStatusEnum.Completed;
            _completedOn = AppUtility.GetDate();
            SaveJob();
        }

        void CheckDownloadRequestStatusAsync()
        {
            Task.Factory.StartNew(() => CheckDownloadRequestStatus());
        }
        void CheckDownloadRequestStatus()
        {
            _downloadRequestCheckTimer.Enabled = true;
            try
            {
                var status = _downloader.GetJobStatus(_jobId);
                if (status == AmazonGlacierDownloadHelper.JobRequestStatusEnum.Completed)
                {
                    JobStatus = JobStatusEnum.ReadyToDownload;                     
                    _downloadRequestCheckTimer.Enabled = false;
                    SaveJob();
                    IsQueued = true;
                }
            }
            catch (Exception ex)
            {
                _downloadRequestCheckTimer.Enabled = false;
                _lastException = ex;
                JobStatus = JobStatusEnum.Error;
                SaveJob();

            }
        }

        public void Initilize()
        {
            Id = Guid.NewGuid();
            JobStatus = JobStatusEnum.Initializing;
            _createdOn = AppUtility.GetDate();
            SaveJob();
            IsQueued = true;
        }
        void OnPauseCommand()
        {
            if (JobStatus != JobStatusEnum.InProgress) return;
            JobStatus = JobStatusEnum.Pausing;
            if (JobType == JobTypeEnum.Upload)
            {
                _uploader.CancelMultiPartUpload();
            }
            else if (JobType == JobTypeEnum.Download)
            {
                _downloader.Cancel();
            }


        }
        void OnRemoveCommand()
        {

            if (OnRemovRequest != null)
                OnRemovRequest(this, null);
        }
        void OnRestartCommand()
        {

            if (JobType == JobTypeEnum.Upload)
            {
                _uploadedBytes = 0;
                _uploadId = null;
                JobStatus = JobStatusEnum.Initializing;
                SaveJob();
                IsQueued = true;
            }
            else if (JobType == JobTypeEnum.Download)
            {
                try
                {
                    if (_downloadedBytes > 0)
                    {
                        if (File.Exists(Path.Combine(DownloadArchiveToPath,DownlodArchiveFileName)))
                            File.Delete(Path.Combine(DownloadArchiveToPath, DownlodArchiveFileName));
                        _downloadedBytes = 0;
                    }
                    _jobId = null;
                    JobStatus = JobStatusEnum.Initializing;
                    SaveJob();
                    IsQueued = true;
                }
                catch (Exception ex)
                {
                    _lastException = ex;
                    JobStatus = JobStatusEnum.Error;
                }

            }


        }
        private void OnResumeCommand()
        {
            if (JobType == JobTypeEnum.Upload)
            {
                if (_uploadId == null)
                {
                    JobStatus = JobStatusEnum.Initializing;
                }
                else
                {
                    JobStatus = JobStatusEnum.InProgress;
                }
            }
            else if (JobType == JobTypeEnum.Download)
            {
                if (_downloadedBytes > 0)
                {
                    JobStatus = JobStatusEnum.InProgress;

                }
                else if (_jobId != null)
                {
                    JobStatus = JobStatusEnum.WaitingForAmzonJobComplete;
                }
                else
                {
                    JobStatus = JobStatusEnum.Initializing;
                }

            }
            IsQueued = true;

        }
        void RequestDownloadAsync()
        {
            Task.Factory.StartNew(() => RequestDownload());
        }

        void RequestDownload()
        {
            try
            {
                _jobId = _downloader.InitiateDownloadRequest(DownloadArchiveId);
                JobStatus = JobStatusEnum.WaitingForAmzonJobComplete;
                SaveJob();
                _downloadRequestCheckTimer.Enabled = true;
            }
            catch (Exception ex)
            {
                _lastException = ex;
                JobStatus = JobStatusEnum.Error;
                SaveJob();
            }
        }
        public void Load(JobDescription job)
        {
            Id = job.Id;
            JobType = job.JobType;
            JobStatus = job.JobStatus;
            VaultName = job.VaultName;
            Region = job.Region;
            Progress = job.Progress;
            _lastException = job.LastException;
            _createdOn = job.CreatedOn;
            _completedOn = job.CompletedOn;
            if (JobType == JobTypeEnum.Upload)
            {
                UploadArchiveDescription = job.UploadArchiveDescription;
                UploadArchiveFilePath = job.UploadArchiveFilePath;
                _uploadId = job.UploadId;
                _uploadedBytes = job.UploadedBytes;
            }
            else if (JobType == JobTypeEnum.Download)
            {
                DownloadArchiveId = job.DownloadArchiveId;
                DownloadArchiveToPath = job.DownloadArchiveToPath;
                DownlodArchiveFileName = job.DownlodArchiveFileName;
                _jobId = job.JobId;
                _downloadedBytes = job.DownloadedBytes;
            }
            if (JobStatus == JobStatusEnum.Pausing)
            {
                JobStatus = JobStatusEnum.Paused;
            }

            IsQueued = true;
        }
        public void SaveJob()
        {
            JobDescription jDesc = new JobDescription();
            jDesc.Id = Id;
            jDesc.JobType = JobType;
            jDesc.JobStatus = JobStatus;
            jDesc.DownloadArchiveId = DownloadArchiveId;
            jDesc.DownloadArchiveToPath = DownloadArchiveToPath;
            jDesc.DownlodArchiveFileName = DownlodArchiveFileName;
            jDesc.DownloadedBytes = _downloadedBytes;
            jDesc.UploadArchiveDescription = UploadArchiveDescription;
            jDesc.UploadArchiveFilePath = UploadArchiveFilePath;
            jDesc.VaultName = VaultName;
            jDesc.Region = Region;
            jDesc.JobId = _jobId;
            jDesc.UploadId = _uploadId;
            jDesc.UploadedBytes = _uploadedBytes;
            if (_lastException != null)
            {
                jDesc.LastException = new Exception(_lastException.Message);
            }
            jDesc.Progress = Progress;
            jDesc.CreatedOn = _createdOn;
            jDesc.CompletedOn = _completedOn;
            AppContext.Current.GetServiceFactory().AppService.SaveJob(jDesc);
        }
        public void Run()
        {
            _uploader.AccessKey = _downloader.AccessKey = AppContext.Current.Settings.AccessKey;
            _uploader.SecretAccessKey = _downloader.SecretAccessKey = AppContext.Current.Settings.SecretAccessKey;
            _isQueued = false;
            if (JobType == JobTypeEnum.Download)
            {
                if (JobStatus == JobStatusEnum.Initializing)
                {
                    RequestDownloadAsync();
                }
                else if (JobStatus == JobStatusEnum.WaitingForAmzonJobComplete)
                {
                    CheckDownloadRequestStatusAsync();
                }
                else if (JobStatus == JobStatusEnum.ReadyToDownload)
                {
                    JobStatus = JobStatusEnum.InProgress;
                    SaveJob();
                    _downloader.DownloadArchive(_jobId, Path.Combine(DownloadArchiveToPath,DownlodArchiveFileName), true);
                }
                else if (JobStatus == JobStatusEnum.InProgress)
                {
                    _downloader.DownloadArchive(_jobId, Path.Combine(DownloadArchiveToPath, DownlodArchiveFileName), true);
                }

            }
            else if (JobType == JobTypeEnum.Upload)
            {
                if (JobStatus == JobStatusEnum.Initializing || JobStatus == JobStatusEnum.InProgress)
                {
                    JobStatus = JobStatusEnum.InProgress;
                    SaveJob();
                    if (_uploadId == null)
                    {
                        _uploader.Upload(UploadArchiveFilePath, UploadArchiveDescription);
                    }
                    else
                    {
                        _uploader.ResumeUpload(UploadArchiveFilePath, _uploadedBytes, _uploadId);
                    }
                }
            }
            NotifyFlagsChanged();
        }

        private string GetStatusMessage()
        {

            var message = "";
            if (JobType == JobTypeEnum.Upload)
            {
                message = "Upload " + AppUtility.GetFileName(UploadArchiveFilePath);
            }
            else
            {
                message = "Download " + AppUtility.GetFileName(Path.Combine(DownloadArchiveToPath, DownlodArchiveFileName));
            }
            if (IsQueued)
            {
                message += ", Queued";
                if (Progress > 0)
                    message += ", " + Progress + "% Complete";
            }
            else if (JobStatus == JobStatusEnum.Initializing)
            {
                message += ", Initializing";
                if (Progress > 0)
                    message += ", " + Progress + "% Complete";
            }
            else if (JobStatus == JobStatusEnum.InProgress)
            {
                message += ", " + Progress + "% Complete";

            }
            else if (JobStatus == JobStatusEnum.Error)
            {
                message += ", Error occured " + GetLastErrorMessage();
                if (Progress > 0)
                    message += ", " + Progress + "% Complete";
            }
            else if (JobStatus == JobStatusEnum.WaitingForAmzonJobComplete || JobStatus == JobStatusEnum.ReadyToDownload)
            {
                message += ", Waiting for amazon job to compete";
            }
            else if (JobStatus == JobStatusEnum.Completed)
            {
                message += ", Completed";
            }
            else if (JobStatus == JobStatusEnum.Pausing)
            {
                message += ", Pausing";
                if (Progress > 0)
                    message += ", " + Progress + "% Complete";
            }
            else if (JobStatus == JobStatusEnum.Paused)
            {
                message += ", Paused";
                if (Progress > 0)
                    message += ", " + Progress + "% Complete";
            }
            return message;
        }
        void SaveUploadHistory(string archiveId)
        {
            try
            {
                var h = new UploadHistory();
                h.Region = Region;
                h.ArchiveId = archiveId;
                h.Date = AppUtility.GetDate();
                h.Description = UploadArchiveDescription;
                h.FilePath = UploadArchiveFilePath;
                h.FileSize = _uploader.FileSize;
                h.VaultName = VaultName;
                AppContext.Current.GetServiceFactory().AppService.SaveHistory(h);
            }
            catch (Exception)
            {

            }
        }
        string GetLastErrorMessage()
        {
            if (_lastException == null) return "";
            return _lastException.Message;
        }
        void WireCommands()
        {
            PauseCommand = new RelayCommand(OnPauseCommand, () => CanPause);
            ResumeCommand = new RelayCommand(OnResumeCommand, () => CanResume);
            RestartCommand = new RelayCommand(OnRestartCommand, () => CanRestart);
            RemoveCommand = new RelayCommand(OnRemoveCommand, () => CanRemove);
        }

        public bool IsWorking
        {
            get { return JobStatus == JobStatusEnum.InProgress || JobStatus == JobStatusEnum.Pausing; }
        }
        void NotifyFlagsChanged()
        {
            RaisePropertyChanged(() => CanPause);
            RaisePropertyChanged(() => CanResume);
            RaisePropertyChanged(() => CanRestart);
            RaisePropertyChanged(() => CanRemove);
        }
        public RelayCommand PauseCommand { get; private set; }
        public RelayCommand ResumeCommand { get; private set; }
        public RelayCommand RestartCommand { get; private set; }
        public RelayCommand RemoveCommand { get; private set; }


        public double Progress { get; private set; }
        private JobStatusEnum _jobStatus;
        public JobStatusEnum JobStatus
        {
            get { return _jobStatus; }
            private set
            {
                if (_jobStatus != value)
                {
                    _jobStatus = value;
                    RaisePropertyChanged(() => JobStatus);
                    RaisePropertyChanged(() => IsWorking);
                    RaisePropertyChanged(() => StatusMessage);
                    NotifyFlagsChanged();
                }
            }
        }
        private JobTypeEnum _jobType;
        public JobTypeEnum JobType
        {
            get { return _jobType; }
            set
            {
                if (_jobType != value)
                {
                    _jobType = value;
                    RaisePropertyChanged(() => JobType);
                }
            }
        }
        public string StatusMessage
        {
            get { return GetStatusMessage(); }

        }

        private bool _isQueued;
        public bool IsQueued
        {
            get { return _isQueued; }
            private set
            {
                if (value != _isQueued)
                {
                    _isQueued = value;
                    RaisePropertyChanged(() => StatusMessage);
                    RaisePropertyChanged(() => IsQueued);
                    NotifyFlagsChanged();
                    if (OnQueued != null && value)
                        OnQueued(this, null);
                }
            }
        }

        public bool CanResume
        {
            get { return JobStatus == JobStatusEnum.Paused || JobStatus == JobStatusEnum.Error; }
        }

        public bool CanRestart
        {
            get { return JobStatus == JobStatusEnum.Paused || JobStatus == JobStatusEnum.Error; }
        }


        public bool CanPause
        {
            get { return JobStatus == JobStatusEnum.InProgress; }
        }

        public bool CanRemove
        {
            get
            {
                return
                    IsQueued || JobStatus == JobStatusEnum.WaitingForAmzonJobComplete
                    || JobStatus == JobStatusEnum.ReadyToDownload
                    || JobStatus == JobStatusEnum.Completed
                    || JobStatus == JobStatusEnum.Paused
                    || JobStatus == JobStatusEnum.Error;

            }
        }

        public Guid Id { get; set; }
        public string DownloadArchiveId { get; set; }
        public string DownloadArchiveToPath { get; set; }
        private string _downlodArchiveFileName;
        public string DownlodArchiveFileName
        {
            get
            {
                if(_downlodArchiveFileName==null)
                {
                    return DownloadArchiveId + ".archive";
                }
                return _downlodArchiveFileName;
            }
            set { _downlodArchiveFileName = value; }
        }
        public string UploadArchiveFilePath { get; set; }
        public string UploadArchiveDescription { get; set; }
        private string _vaultName;
        public string VaultName
        {
            get { return _vaultName; }
            set
            {
                _vaultName = value;
                _uploader.VaultName = value;
                _downloader.VaultName = value;
            }
        }

        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (_isSelected != value)
                {
                    _isSelected = value;
                    RaisePropertyChanged(() => IsSelected);
                }
            }
        }
        public bool IsJobSaved { get; set; }
        private AmazonRegionEnum _region;
        public AmazonRegionEnum Region
        {
            get { return _region; }
            set
            {
                if (_region != value)
                {
                    _region = value;
                    _downloader.Region = _uploader.Region = value;
                    RaisePropertyChanged(() => Region);
                }
            }
        }

    }
    
}
